//C- -------------------------------------------------------------------
//C- Java DjVu (r) (v. 0.8)
//C- Copyright (c) 2004-2005 LizardTech, Inc.  All Rights Reserved.
//C- Java DjVu is protected by U.S. Pat. No.C- 6,058,214 and patents
//C- pending.
//C-
//C- This software is subject to, and may be distributed under, the
//C- GNU General Public License, Version 2. The license should have
//C- accompanied the software or you may obtain a copy of the license
//C- from the Free Software Foundation at http://www.fsf.org .
//C-
//C- The computer code originally released by LizardTech under this
//C- license and unmodified by other parties is deemed "the LIZARDTECH
//C- ORIGINAL CODE."  Subject to any third party intellectual property
//C- claims, LizardTech grants recipient a worldwide, royalty-free,
//C- non-exclusive license to make, use, sell, or otherwise dispose of
//C- the LIZARDTECH ORIGINAL CODE or of programs derived from the
//C- LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
//C- General Public License.   This grant only confers the right to
//C- infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
//C- the extent such infringement is reasonably necessary to enable
//C- recipient to make, have made, practice, sell, or otherwise dispose
//C- of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
//C- any greater extent that may be necessary to utilize further
//C- modifications or combinations.
//C-
//C- The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
//C- OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//C- TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
//C- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//C-
//C- In addition, as a special exception, LizardTech Inc. gives permission
//C- to link the code of this program with the proprietary Java
//C- implementation provided by Sun (or other vendors as well), and
//C- distribute linked combinations including the two. You must obey the
//C- GNU General Public License in all respects for all of the code used
//C- other than the proprietary Java implementation. If you modify this
//C- file, you may extend this exception to your version of the file, but
//C- you are not obligated to do so. If you do not wish to do so, delete
//C- this exception statement from your version.
//C- -------------------------------------------------------------------
//C- Developed by Bill C. Riemers, Foxtrot Technologies Inc. as work for
//C- hire under US copyright laws.
//C- -------------------------------------------------------------------
//
package com.lizardtech.djvu{
import flash.utils.ByteArray;

import java.io.*;
import java.net.*;
import java.util.*;



/**
 * This class implements a random access mapping of input streams and URL 
 * connections.  For input streams and non-http connections, the mapping 
 * is achieved by reading in all the data into memory.  Http streams are
 * mapped by reading in data blocks when needed.
 *
 * @author Bill C. Riemers
 * @version $Revision: 1.13 $
 */
public class DataPool
  extends DjVuObject
{
  //~ Static fields/initializers ---------------------------------------------

  /** The default size of each block. */
  public static const BLOCKSIZE:int= 8192;
    
  /** Object for caching raw data. ! */
  public static var cache:Hashtable= new Hashtable();

  //~ Instance fields --------------------------------------------------------

  // This contains the data we a buffering.
  private var buffer:Vector= new Vector();

  // The end of the stream, or a number larger than the end of the stream.
  private var endOffset:int= Integer.MAX_VALUE;
  
  // The url we are reading.
  private var url:URL=null;
  
  // The input stream we are reading.
  public var input:InputStream = new InputStream();
  
  // True if we might be able random access memory blocks on a server.
  private var rangeAccepted:Boolean=false;
  
  // The pointer for a simple cache of blocks reciently accessed.
  private var cacheAccessIndex:int=0;

  // A simple cache of blocks accessed.
  private var cacheAccessArray:Array=new Array(256);
  
  // The pointer for a simple cache of blocks created.
  private var cacheCreatedIndex:int=0;
  
  // A simple cache of blocks created.
  private var cacheCreatedArray:Array=new Array(256);
  
  // The largest end offset of read data.
  private var currentSize:int=0;
  
  //~ Constructors -----------------------------------------------------------

  /**
   * Creates a new DataPool object.
   */
  public function DataPool()
  {
  }

  //~ Methods ----------------------------------------------------------------

  /**
   * Creates an instance of DataPool with the options interherited from the
   * specified reference.
   * 
   * @param ref Object to interherit DjVuOptions from.
   * 
   * @return a new instance of DataPool.
   */
  public static function createDataPool(ref:DjVuInterface):DataPool{
    var options:DjVuOptions= ref.getDjVuOptions();

    return create(
      options,
      options.getDataPoolClass(),
      "com.lizardtech.djvu.DataPool") as DataPool;
  }

  
    /**
   * Initialize this map to read the specified URL. If a cached DataPool for this 
   * URL exists, it will be returned.
   * 
   * @param url the URL to read
   * 
   * @return an initialized DataPool
   */
  public final function init(inputstream:InputStream):DataPool{
    this.input = inputstream;
    return this;
  }
  
  
  /** 
   * Query the largest read end offset.
   *
   * @return the largest read end offset
   */
  public function getCurrentSize():int{
    return currentSize;
  }
  
  /**
   * Request the specified block of data. Data may be buffered, or read.
   *
   * @param index the position of the block start position divided by BLOCKSIZE.
   * @param read True if unavailable blocks should be read from the data source.
   *
   * @return a byte array up to size BLOCKSIZE, or null if no data is available.
   */
  public final function getBlock(index:int, read:Boolean):ByteArray
  {
    var start:int=index*BLOCKSIZE;
    if((index < 0)||(start >= endOffset))
    {
      return null;
    }
    if(index < buffer.size()) {
      var block:Object=buffer.elementAt(index);
      if(block != null) {
        if(block instanceof ByteArray)
        {
          return block as ByteArray;
        }
        block = getFromReference(block);
        if(block != null) {
          return block as ByteArray;
        }
      }
    }
    return read?readBlock(index):null;
  }

  // Read the specified block of data.  Synchronization should happen prior to calling this
  // routine.  Data may be read either sequentially, or in random order if the server supports
  // http 1.1 range specifiers.
  private function readBlock(index:int):ByteArray
  {
    var retval:ByteArray=getBlock(index,false);
    if(retval == null) { // need to fetch remote data
      
      var start:int=index*BLOCKSIZE;
      var end:int=(index+1)*BLOCKSIZE;
      
      retval = new ByteArray();
      
      // fetch block
      while((start < endOffset) && (start < end) ) {
        	input.readBytes(retval, start, BLOCKSIZE);
        	start += retval.length;
      }
      if(retval != null) {
        if(buffer.size() <= index) {
           buffer.setElementAt(retval, index);          
        }
        if(end > currentSize) {
          currentSize=end;
        }
      }
    }
    return retval;
  }

  /**
   * Set the end position.  This value may only be reduced, never increased.
   *
   * @param offset new end offset
   */
  protected final function setEndOffset(offset:int):void{
    if(offset < endOffset)
    {
      endOffset=offset;
      var size:int=(offset+BLOCKSIZE-1)/BLOCKSIZE;
      if(size > buffer.size())
      {
        buffer.setSize(size);
      }
    }
  }
  
  /**
   * Query the size of this vector.
   *
   * @return the size of this vector
   */
  public function getEndOffset():int{
    return endOffset;
  }


}}
